﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Toolkit.Web.Infastructure;
using Toolkit.Web.ExtensionMethods;
using Toolkit.Web.Helpers;
using Toolkit.Web.Resources;
using System.Web;

namespace Toolkit.Web.ViewComponents.DataGrid
{
    public sealed class DataGridPaginationHtmlBuilder
    {
        public DataGridPaginationHtmlBuilder(DataGridPagination pagination)
        {
            if (pagination == null)
            {
                throw new ArgumentNullException("pagination");
            }

            _pagination = pagination;
            CssClassNames = new ComponentCssClassNames();
        }

        private DataGridPagination _pagination
        {
            get;
            set;
        }

        public ComponentCssClassNames CssClassNames
        {
            get;
            private set;
        }

        public IList<int> PriorVisiblePageOptions
        {
            get;
            private set;
        }

        public IList<int> PostVisiblePageOptions
        {
            get;
            private set;
        }

        public IHtmlNode Html()
        {
            PriorVisiblePageOptions = _pagination.GetPriorVisibleOffsetPageOptions();
            PostVisiblePageOptions = _pagination.GetPostVisibleOffsetPageOptions();

            return PaginationMarkup();
        }

        public IHtmlNode PaginationMarkup()
        {
            IHtmlNode htmlTag = new HtmlNode("div");

            if (_pagination.HtmlAttributes != null)
            {
                htmlTag.MergeAttributes(_pagination.HtmlAttributes, true);
            }

            htmlTag.AddClass(CssClassNames.Wrapper);
            htmlTag.AddStyle("position", "relative");

            if (_pagination.EnablePageIndexSelection)
            {
                htmlTag.Append(PageIndexSelection());
            }

            if (_pagination.EnablePageSizeSelection)
            {
                htmlTag.Append(PageSizeSelection());
            }

            return htmlTag;
        }

        public IHtmlNode PageSizeSelection()
        {
            IHtmlNode htmlTag = new HtmlNode("div");
            htmlTag.AddClass(CssClassNames.PageSizeSelectionWrapper);
            htmlTag.Append(PageSizeSelectionInput());
            return htmlTag;
        }

        public IHtmlNode PageSizeSelectionInput()
        {
            IHtmlNode htmlTag = new HtmlNode("div");
            htmlTag.AddClass(CssClassNames.PaginationPart);

            IHtmlNode select = new HtmlNode("select");
            select.Attributes["name"] = Guid.NewGuid().ToString();

            // Make sure selected page size exists within the available page size option collection.
            if (!_pagination.PageSizeOptions.Contains(_pagination.PageSize))
            {
                _pagination.PageSizeOptions.Add(_pagination.PageSize);
            }

            foreach (int pageSize in _pagination.PageSizeOptions.OrderBy(option => option))
            {
                IHtmlNode optionTag = new HtmlNode("option");
                optionTag.Attributes["value"] = pageSize.ToString();
                optionTag.InnerHtml = pageSize.ToString();

                if (pageSize == _pagination.PageSize)
                {
                    optionTag.Attributes["selected"] = "selected";
                }

                select.Append(optionTag);
            }

            htmlTag.Append(select);

            return htmlTag;
        }

        public IHtmlNode PageIndexSelection()
        {
            IHtmlNode htmlTag = new HtmlNode("div");
            htmlTag.AddClass(CssClassNames.PageIndexSelectionWrapper);

            // Hide page selection markup if there is no point in showing it (only one page).
            if (_pagination.NumberOfPages.HasValue && _pagination.NumberOfPages.Value == 1 && _pagination.IsFirstPage)
            {
                htmlTag.AddStyle("display", "none");
            }

            IList<IHtmlNode> parts = new List<IHtmlNode>();

            parts.Add(FirstPagePart());
            parts.Add(PreviousPagePart());

            if (_pagination.VisiblePageOffset > 0 && _pagination.NumberOfPages.HasValue)
            {
                parts.Add(PriorOffsetPagesPart());
            }

            parts.Add(CurrentPagePart());

            if (_pagination.VisiblePageOffset > 0 && _pagination.LastPageIndex.HasValue)
            {
                parts.Add(PostOffsetPagesPart());
            }

            parts.Add(NextPagePart());

            if (_pagination.LastPageIndex.HasValue)
            {
                parts.Add(LastPagePart());
            }

            foreach (IHtmlNode node in parts)
            {
                htmlTag.Append(node);
            }

            return htmlTag;
        }

        public IHtmlNode FirstPagePart()
        {
            IHtmlNode htmlTag = new HtmlNode("div");
            htmlTag.AddClass(CssClassNames.PaginationPart);
            htmlTag.AddClass(CssClassNames.FirstPage);

            if (_pagination.IsFirstPage)
            {
                htmlTag.AddClass(CssClassNames.DisabledPage);
            }

            IHtmlNode button = new HtmlNode("span");
            button.AddClass(CssClassNames.PageLink);
            button.Attributes["data-page-index"] = _pagination.FirstPageIndex.ToString();
            button.Attributes["title"] = HttpUtility.HtmlEncode(Language.ViewComponents_DataGrid_First);
            button.InnerHtml = HttpUtility.HtmlEncode(Language.ViewComponents_DataGrid_First);

            htmlTag.Append(button);

            return htmlTag;
        }

        public IHtmlNode PreviousPagePart()
        {
            IHtmlNode htmlTag = new HtmlNode("div");
            htmlTag.AddClass(CssClassNames.PaginationPart);
            htmlTag.AddClass(CssClassNames.PreviousPage);

            if (_pagination.IsFirstPage)
            {
                htmlTag.AddClass(CssClassNames.DisabledPage);
            }

            IHtmlNode button = new HtmlNode("span");
            button.AddClass(CssClassNames.PageLink);
            button.Attributes["data-page-index"] = _pagination.PreviousPageIndex.ToString();
            button.Attributes["title"] = HttpUtility.HtmlEncode(Language.ViewComponents_DataGrid_Previous);
            button.InnerHtml = HttpUtility.HtmlEncode(Language.ViewComponents_DataGrid_Previous);

            htmlTag.Append(button);

            return htmlTag;
        }

        public IHtmlNode PriorOffsetPagesPart()
        {
            IHtmlNode htmlTag = new HtmlNode("div");
            htmlTag.AddClass(CssClassNames.PaginationPart);
            htmlTag.AddClass(CssClassNames.OffsetPages);

            IList<IHtmlNode> innerTags = new List<IHtmlNode>();

            // Always include the initial page 1 link if it will not be included in the visible offset page list (ex 1 ... 3 4 5).
            if (!PriorVisiblePageOptions.Contains(_pagination.FirstPageIndex) && !_pagination.IsFirstPage)
            {
                IHtmlNode button = new HtmlNode("span");
                button.AddClass(CssClassNames.PageLink);
                button.Attributes["data-page-index"] = _pagination.FirstPageIndex.ToString();
                button.InnerHtml = (_pagination.FirstPageIndex + 1).ToString();
                innerTags.Add(button);
            }

            // Build hidden page(s) indicator if applicable.
            if (_pagination.DoPriorHiddenPageOptionsExist())
            {
                IHtmlNode button = new HtmlNode("span");
                button.AddClass(CssClassNames.HiddenPages);
                button.InnerHtml = "...";
                innerTags.Add(button);
            }

            // Build visible offset page(s) if applicable. 
            for (int i = (_pagination.PageIndex - PriorVisiblePageOptions.Count); i < _pagination.PageIndex; i++)
            {
                IHtmlNode button = new HtmlNode("span");
                button.AddClass(CssClassNames.PageLink);
                button.Attributes["data-page-index"] = i.ToString();
                button.InnerHtml = (i + 1).ToString();
                innerTags.Add(button);
            }

            foreach (IHtmlNode node in innerTags)
            {
                htmlTag.Append(node);
            }

            return htmlTag;
        }

        public IHtmlNode CurrentPagePart()
        {
            IHtmlNode htmlTag = new HtmlNode("div");
            htmlTag.AddClass(CssClassNames.PaginationPart);
            htmlTag.AddClass(CssClassNames.CurrentPage);

            IHtmlNode inputTag = new HtmlNode("input", TagRenderMode.SelfClosing);
            inputTag.Attributes["name"] = Guid.NewGuid().ToString();
            inputTag.Attributes["type"] = "text";
            inputTag.Attributes["value"] = (_pagination.PageIndex + 1).ToString();

            htmlTag.Append(inputTag);

            return htmlTag;
        }

        public IHtmlNode PostOffsetPagesPart()
        {
            IHtmlNode htmlTag = new HtmlNode("div");
            htmlTag.AddClass(CssClassNames.PaginationPart);
            htmlTag.AddClass(CssClassNames.OffsetPages);

            IList<IHtmlNode> innerTags = new List<IHtmlNode>();

            // Build visible offset page(s) if applicable. 
            for (int i = (_pagination.PageIndex + 1); i <= (_pagination.PageIndex + PostVisiblePageOptions.Count); i++)
            {
                IHtmlNode button = new HtmlNode("span");
                button.AddClass(CssClassNames.PageLink);
                button.Attributes["data-page-index"] = i.ToString();
                button.InnerHtml = (i + 1).ToString();
                innerTags.Add(button);
            }

            // Build hidden page(s) indicator if applicable.
            if (_pagination.DoPostHiddenPageOptionsExist())
            {
                IHtmlNode button = new HtmlNode("span");
                button.AddClass(CssClassNames.HiddenPages);
                button.InnerHtml = "...";
                innerTags.Add(button);
            }

            // Always include the last page index link if it will not be included in the visible offset page list (ex 5 6 7 ... 11).
            if (!PostVisiblePageOptions.Contains(_pagination.LastPageIndex.Value) && !_pagination.IsLastPage)
            {
                IHtmlNode button = new HtmlNode("span");
                button.AddClass(CssClassNames.PageLink);
                button.Attributes["data-page-index"] = _pagination.LastPageIndex.Value.ToString();
                button.InnerHtml = (_pagination.LastPageIndex.Value + 1).ToString();
                innerTags.Add(button);
            }

            foreach (IHtmlNode node in innerTags)
            {
                htmlTag.Append(node);
            }

            return htmlTag;
        }

        public IHtmlNode NextPagePart()
        {
            IHtmlNode htmlTag = new HtmlNode("div");
            htmlTag.AddClass(CssClassNames.PaginationPart);
            htmlTag.AddClass(CssClassNames.NextPage);

            if (_pagination.IsLastPage)
            {
                htmlTag.AddClass(CssClassNames.DisabledPage);
            }

            IHtmlNode button = new HtmlNode("span");
            button.AddClass(CssClassNames.PageLink);
            button.Attributes["data-page-index"] = _pagination.NextPageIndex.ToString();
            button.Attributes["title"] = HttpUtility.HtmlEncode(Language.ViewComponents_DataGrid_Next);
            button.InnerHtml = HttpUtility.HtmlEncode(Language.ViewComponents_DataGrid_Next);

            htmlTag.Append(button);

            return htmlTag;
        }

        public IHtmlNode LastPagePart()
        {
            IHtmlNode htmlTag = new HtmlNode("div");
            htmlTag.AddClass(CssClassNames.PaginationPart);
            htmlTag.AddClass(CssClassNames.LastPage);

            if (_pagination.IsLastPage)
            {
                htmlTag.AddClass(CssClassNames.DisabledPage);
            }

            IHtmlNode button = new HtmlNode("span");
            button.AddClass(CssClassNames.PageLink);
            button.Attributes["data-page-index"] = _pagination.LastPageIndex.ToString();
            button.Attributes["title"] = HttpUtility.HtmlEncode(Language.ViewComponents_DataGrid_Last);
            button.InnerHtml = HttpUtility.HtmlEncode(Language.ViewComponents_DataGrid_Last);

            htmlTag.Append(button);

            return htmlTag;
        }

        public class ComponentCssClassNames
        {
            public readonly string Wrapper = "vc-dg-pager";
            public readonly string PaginationPart = "vc-dg-pagerPart";
            public readonly string PageSizeSelectionWrapper = "vc-dg-pageSize";
            public readonly string PageIndexSelectionWrapper = "vc-dg-pageIndex";
            public readonly string FirstPage = "vc-dg-firstPage";
            public readonly string PreviousPage = "vc-dg-prevPage";
            public readonly string LastPage = "vc-dg-lastPage";
            public readonly string NextPage = "vc-dg-nextPage";
            public readonly string CurrentPage = "vc-dg-currentPage";
            public readonly string HiddenPages = "vc-dg-hiddenPages";
            public readonly string OffsetPages = "vc-dg-offsetPages";
            public readonly string PageLink = "vc-dg-pageLink";
            public readonly string DisabledPage = "vc-dg-disabledPage";
        }
    }
}
